#include <iostream>
#include <map>
#include <fstream>
#include <ctime>
#include <unistd.h>
#include <cstdlib>
#include <vector>
#include <ctype.h>
#include <cmath>
#include <omp.h>
#include <algorithm>

using namespace std;


typedef struct word
{
	string st;
	int occurrences;
};


bool compare(word *a, word *b)
{
	return (a->occurrences > b->occurrences);
}

int main(int argc, char *argv[])
{    
	//Ponteiro para arquivo
	FILE *fp;
	//caracter usado para ler arquivo
	char c;
    //Mapa de palavras - ate 5 letras
    map<string,int*> map_words;
    //Iterador
    map<string,int*>::iterator it;
    //Retorno de inserção
    pair<map<string,int*>::iterator,bool> ret;
    //tamanho máximo das palavras do texto
    int max = 0;
    //Stream para escrever em arquivos
    ofstream f;

    //Contador de palavras encontradas
    int found = 0;

    //vetor por tamanho de palavras
    vector<vector<string*> > words_by_size;

    //vetor DE tamanho de palavras
    vector<int> size_of_words;
    //Vetores de prefixos e sufixos
    vector<word*> prefixes, sufixes;


    cout << "Preprocessamento do arquivo... " << endl;

    srand(time(NULL));
	//abertura do arquivo
    fp = fopen("palavras.txt", "r");
    //preprocessa o arquivo
    do
    {
        string str;
        c = fgetc(fp);
        //Enquanto nao encontra um símbolo de sincronia, consome
        while(!isalpha(c) && c != EOF)
        {
            c = fgetc(fp);
        }
        //Se encontrou fim de linha, para a execucao do laco
        if(c == EOF)
            break;
        //Monta a palavra ate encontrar um simbolo nao permitido
        while((isalnum(c) || c == '-') && c != EOF)
        {
            str += toupper(c);
            c = fgetc(fp);
            while(isdigit(c) || c == '-')
            {
                c = fgetc(fp);
            }
        }
        //Se encontrou fim de linha, para a execucao do laco
        if(c == EOF)
            break;

        int strsize = str.size();
        //Aumenta o vetor se |palavra| > tamanho do vetor para |palavra|
        if(strsize > max)
        {
            for(int i = 0; i < (strsize-max); i++)
            {
                words_by_size.push_back(vector<string*>());
                size_of_words.push_back(0);
            }
            //Atribui |palavra| para o tamanho do vetor
            max = strsize;
        }
        int *vn = (int *)malloc(3*sizeof(int));
        vn[0] = vn[1] = vn[2] = 0;
        //Insere a palavra no mapa, se possível
        ret = map_words.insert(pair<string,int*>(str,vn));
        //Se foi possível, ret.second vale TRUE
        if(ret.second)
        {
            string *aux_str = new string(str);
            words_by_size[strsize-1].push_back(aux_str);
            size_of_words[strsize-1]++;
        }
        else
            free(vn);

    }while(c != EOF);

	
    //Limpa o mapa
    map_words.clear();
    //Reinsere os dados nos respectivos mapas, das palavras menores para as palavras
    //maiores, contabilizando corretamente a incidencia de prefixos e sufixos
    for(int i = 0; i < words_by_size.size(); i++)
    {
        for(int j = 0; j < words_by_size[i].size(); j++)
        {
            int *vn = (int *)malloc(3*sizeof(int));
            vn[0] = vn[1] = vn[2] = 0;
            string st = *words_by_size[i][j];
            map_words.insert(pair<string,int*>(st, vn));
            for(int k = i; k > 0; k--)
            {
                it = map_words.find(st.substr(0,k));
                if(it != map_words.end())
                    it->second[1]++;
            }
            for(int k = 1; k <= i; k++)
            {
                it = map_words.find(st.substr(k,i+1));
                if(it != map_words.end())
                    it->second[2]++;
            }
        }
    }

    f.open("map");
    //Insere todos os dados do mapa nos vetores de prefixos e sufixos - até 5 letras
    //Tambem escreve o arquivo correspondente
    for(it=map_words.begin(); it != map_words.end(); it++)
    {
        word *pref = new word();
        word *suf = new word();
        //Pega a string
        pref->st = suf->st = it->first;
        //Pega a incidencia de prefixos
        pref->occurrences = it->second[1];
        //Pega a incidencia de sufixos
        suf->occurrences = it->second[2];
        //Escreve o arquivo
        f << it->first << " " << it->second[0] << " " << it->second[1] << " " << it->second[2] << endl;
        prefixes.push_back(pref);
        sufixes.push_back(suf);
    }
    f.close();

    //Ordena os vetores por ocorrencia [palavras com mais incidencia primeiro]
    sort(prefixes.begin(), prefixes.end(), compare);
    sort(sufixes.begin(), sufixes.end(), compare);

    //Escreve o arquivo de prefixos
    f.open("prefix");
    int cnt = 0;
    while(prefixes.at(cnt)->occurrences > 0)
    {
        f << prefixes.at(cnt)->st << " " << prefixes.at(cnt)->occurrences << endl;
        cnt++;
    }
    //Remove as palavras que não tem incidencia como prefixo
    while(prefixes.size() > cnt)
        prefixes.erase(prefixes.begin()+cnt);
    f.close();

    //Escreve o arquivo de sufixos
    f.open("sufix");
    cnt = 0;
    while(sufixes.at(cnt)->occurrences > 0)
    {
        f << sufixes.at(cnt)->st << " " << sufixes.at(cnt)->occurrences << endl;
        cnt++;
    }
    //Remove as palavras que não tem incidencia como sufixo
    while(sufixes.size() > cnt)
        sufixes.erase(sufixes.begin()+cnt);
    f.close();

    cout << "Finalizado!" << endl << endl;

	return 0;

}